#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/signal.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/device.h>
#include <linux/pci.h>
#include <linux/interrupt.h> 
#include <asm/uaccess.h> 
 
#include <linux/jiffies.h>
#include <linux/semaphore.h>

#include "ioctl_drv_usr.h"


#define DEBUG  
#ifdef DEBUG
	#define DEBUG_ERR(format,args...) \
	do{  \
		printk("[%s:%d] ",__FUNCTION__,__LINE__); \
		printk(format,##args); \
	}while(0)
#else
	#define DEBUG_PRINT(format,args...) 
#endif

void printk_dbg(unsigned char* buf, int count)
{
	int i = 0;
	
	for (i=0; i<count; i+=16)
	{
		printk("%02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X", \
			   buf[i],buf[i+1],buf[i+2],buf[i+3],      buf[i+4],buf[i+5],buf[i+6],buf[i+7],   \
			   buf[i+8],buf[i+9],buf[i+10],buf[i+11],  buf[i+12],buf[i+13],buf[i+14],buf[i+15]);
	}
	printk("\n");
	return;
}

void printk_dbg2(unsigned char* buf, int start, int count)
{
	int i = 0;
	
	for (i=start; i<(start+count); i+=16)
	{
		printk("%02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X", \
			   buf[i],buf[i+1],buf[i+2],buf[i+3],      buf[i+4],buf[i+5],buf[i+6],buf[i+7],   \
			   buf[i+8],buf[i+9],buf[i+10],buf[i+11],  buf[i+12],buf[i+13],buf[i+14],buf[i+15]);
	}
	printk("\n");
	return;
}

#define DEV_NAME "fpga_pcie"


static struct class * fpga_class;
static struct device * fpga_class_dev;
 
struct fpga_device
{
	struct pci_dev* pci_dev;
	struct cdev cdev;
	dev_t devno;
} fpga_dev;

//
//bar_n(n=0,1,2或者0，1，2，3，4，5) 空间的物理地址，长度，虚拟地址
//
unsigned long bar0_phy;
unsigned long bar0_vir;
unsigned long bar0_length;

extern u32 g_irq_time;
extern void test_trig_pcie_irq(void);
extern void test_clr_trig_pcie_irq(void);
extern u32 test_rd_irq_start_time(void);
extern u32 test_rd_irq_end_time(void);
extern u32 test_get_irq_time(void);


//
// 根据设备的id填写,这里假设厂商id和设备id
//
#define FPGA_VENDOR_ID    0x10EE
#define FPGA_DEVICE_ID    0x7024
   
#define DEVICE_NUMBER      1

static volatile int ev_irq_msi = 0; 
static wait_queue_head_t  irq_msi_waitq;
unsigned char * g_pFpga = NULL;

static struct pci_device_id fpga_ids[] = 
{	
    {FPGA_VENDOR_ID, FPGA_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0,0,0},
    {0,}
};
	
MODULE_DEVICE_TABLE(pci, fpga_ids);

/*
functions prototype
*/
static int fpga_mem_init(void);

static int fpga_probe(struct pci_dev *pdev, const struct pci_device_id *id);
static void fpga_remove(struct pci_dev *pdev);

static irqreturn_t fpga_interrupt(int irq, void * dev);

void pcie_bar0_set(int offset, unsigned int val);
unsigned int pcie_bar0_get(int offset);

/*
functions:
*/
void pcie_bar0_set(int offset, unsigned int val)
{
	if (bar0_vir == 0)
	{
		printk("fail: pcie_bar0_set->bar0_vir=0! \n");
	}
	else
	{
		*(unsigned int *)(bar0_vir + offset) = val;
	}
	return;
}

unsigned int pcie_bar0_get(int offset)
{
	if (bar0_vir == 0)
	{
		printk("fail: pcie_bar0_get->bar0_vir=0! \n");
		return 0;
	}
	else
	{
		return *(unsigned int *)(bar0_vir + offset);
	}	
}


static int fpga_mmap(struct file *filp, struct vm_area_struct *vma)
{
	return (fpga_share_mem_mmap_all(vma));
}

static int fpga_pcie_exist = 0;
static int fpga_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
	int i;
	int ret;
	int irq;

	//
	// 使能pci设备
	//
	if (pcim_enable_device(pdev))
	{
		printk("failed: pcim_enable_devic! \n");
        ret = -EIO;
		return ret;
	}
	else
	{
		printk("===pcim_enable_device ok!=== \n");
	}

	//
	// enable pci master
	//
	pci_set_master(pdev);	
	fpga_dev.pci_dev = pdev;

#if 1
	//ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
	ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
	if (ret < 0)
	{
		printk("failed: pci_alloc_irq_vectors \n");
		
		pci_disable_device(pdev);
		return ret;
	}
	printk("pci_alloc_irq_vectors(ret-%d) ok! \n", ret); 	 
	
	pdev->irq = pci_irq_vector(pdev, 0);
	printk("pci_irq_vector(%d) ok! \n", pdev->irq); 	 

	//
	// 注册irq
	//
	ret = devm_request_irq(&pdev->dev, pdev->irq, fpga_interrupt, 0, DEV_NAME, NULL);
    if (ret)
	{
		printk("failed: request_irq\n");
		
		pci_free_irq_vectors(pdev);
		pci_disable_device(pdev);
		return ret;
    }
	else
	{
		printk("request_irq(%d) ok! \n", irq);		
	}	
#endif	

	if (unlikely(pci_request_regions(pdev, DEV_NAME)))
	{
		printk("failed: pci_request_regions \n");
		ret = -EIO;
		
		pci_disable_device(pdev);
		return ret;
	}

	//
	//获得bar0的物理地址和虚拟地址
	//
	bar0_phy = pci_resource_start(pdev, 0);
	if (bar0_phy < 0)
	{
		printk("failed: pci_resource_start\n");
		ret = -EIO;
		
		pci_release_regions(pdev);
		pci_disable_device(pdev);
		return ret;
	}
	
	//
	//假设bar0是作为内存，流程是这样的，但是在本程序中不对bar0进行任何操作。
	//
	bar0_length = pci_resource_len(pdev, 0);
	if (bar0_length != 0)
	{
		bar0_vir = (unsigned long)ioremap(bar0_phy, bar0_length);
		if (bar0_vir == 0)
		{
			printk("failed:ioremap bar0 \n");
			ret = -EIO;
			
			pci_release_regions(pdev);
			return ret;
		}		
	}	
	printk("Bar0=<phy:0x%lX, virt:0x%lX, len:0x%lX \n", bar0_phy, bar0_vir, bar0_length);	

	//
	// kmalloc ddr 
	//
	kmalloc_dma_init_all();

	//
	// start dma
	//
	kmalloc_dma_rx_start_all();

	fpga_pcie_exist = 1;

	return ret;
}

static void fpga_remove(struct pci_dev *pdev)
{
	//free_irq(pdev->irq, fpga_dev.pci_dev);
	free_irq(pdev->irq, NULL); /* tgsp add: it must require */
	
	//pci_disable_msi(pdev);
 	pci_free_irq_vectors(pdev);
	
	iounmap((void *)bar0_vir);
	
	pci_release_regions(pdev);
	pci_disable_device(pdev);
	return;
}
 
static struct pci_driver fpga_driver = 
{
    .name = DEV_NAME,
    .id_table = fpga_ids,
    
    .probe = fpga_probe,
    .remove = fpga_remove,
};
 
static int fpga_open(struct inode *inode, struct file *file)
{
	printk("\n driver: fpga_open \n");
	
	//填写产品的逻辑
	return 0;
}
 
static int fpga_close(struct inode *inode, struct file *file)
{
	printk("driver: fpga_close \n\n");
	
	//填写产品的逻辑
	return 0;
}

#undef     TEST_MODE_1
#define    TEST_MODE_2

static ssize_t fpga_read(struct file *file, char __user *buf, 
                            size_t count, loff_t *ppos)
{
	printk("driver: fpga_read\n");
    return count;
}

static ssize_t fpga_write(struct file *file, const char __user *buf,
                              size_t count, loff_t *ppos)
{
	printk("driver: fpga_write\n");
    return count;
}

static loff_t fpga_llseek(struct file *file, loff_t off, int whence)
{
    return off;
}

#if 1


static int rx_chn = 0;
static irqreturn_t fpga_interrupt(int irq, void * dev)
{ 
	printk("irq coming \n");

	rx_chn = pcie_bar0_get(DMA_WR_CHANNEL);
	switch (rx_chn)
	{
	case 1:    // chn-1	
		printk("irq->chn_1");
		irq_dma_rx_start_chn_1();
		break;
	
	case 2:    // chn-2
		printk("irq->chn_2");
		irq_dma_rx_start_chn_2();
		break;
	
	case 3:    // chn-3
		printk("irq->chn_3");
		irq_dma_rx_start_chn_3();
		break;
		
	case 4:    // chn-4
		printk("irq->chn_4");
		irq_dma_rx_start_chn_4();
		break;
		
	case 5:    // chn-5
		printk("irq->chn_5");
		irq_dma_rx_start_chn_5();
		break;
		
	case 6:    // chn-6
		printk("irq->chn_6");
		irq_dma_rx_start_chn_6();
		break;
		
	case 7:    // chn-7
		printk("irq->chn_7");
		irq_dma_rx_start_chn_7();
		break;
		
	default:
		printk("err:irq->chn_x(%d) \n", rx_chn);
		break;
	}

	//
	// irq to wake usr polling
	//
	ev_irq_msi = 1;
	wake_up_interruptible(&irq_msi_waitq);

#if 1
	test_clr_trig_pcie_irq();
	g_irq_time = test_rd_irq_end_time() - test_rd_irq_start_time();
#endif

	printk("irq proc end! \n");
	return IRQ_RETVAL(IRQ_HANDLED); 	
}

T_DDR_DATA g_ddr_data;

static long fpga_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	//填写产品的逻辑
	//为应用层提供的函数接口，通过解析cmd，在switch中做出不同的处理。
	int ret = 0;
	int i = 0;
	unsigned long phys = 0;
	unsigned char * ddr_virt = NULL;
	unsigned char * normal_virt	= (unsigned char *)virt_addr_kern_get(0);
	unsigned long offset = 0;
	
	switch (cmd)
	{		
	case IO_GET_RD_DDR_TEST:
		printk("==ioctl: IO_GET_RD_DDR_TEST(0x%X)== \n", IO_GET_RD_DDR_TEST);
		ddr_virt = (unsigned char *)normal_virt;
		phys = (unsigned long)virt_addr_kern_get(0);
		
		g_ddr_data.addr = phys; 
		for (i=0; i<BUF_SIZE; i++)
		{
			g_ddr_data.buf[i] = ddr_virt[i];  // read data from ddr
		}
#if 1
		printk("ddr kern:phy-0x%lX,virt-0x%lX \n", phys, normal_virt);
		for (i=0; i<128; i+=16)
		{
			printk("%04X: %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X", \
				   i,   \
				   ddr_virt[i],ddr_virt[i+1],ddr_virt[i+2],ddr_virt[i+3],	  \
				   ddr_virt[i+4],ddr_virt[i+5],ddr_virt[i+6],ddr_virt[i+7],   \
				   ddr_virt[i+8],ddr_virt[i+9],ddr_virt[i+10],ddr_virt[i+11],	\
				   ddr_virt[i+12],ddr_virt[i+13],ddr_virt[i+14],ddr_virt[i+15]);			
		}
		printk("... ...");
		for (i=(BUF_SIZE-128); i<BUF_SIZE; i+=16)
		{
			printk("%04X: %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X", \
				   i,   \
				   ddr_virt[i],ddr_virt[i+1],ddr_virt[i+2],ddr_virt[i+3],	  \
				   ddr_virt[i+4],ddr_virt[i+5],ddr_virt[i+6],ddr_virt[i+7],   \
				   ddr_virt[i+8],ddr_virt[i+9],ddr_virt[i+10],ddr_virt[i+11],	\
				   ddr_virt[i+12],ddr_virt[i+13],ddr_virt[i+14],ddr_virt[i+15]);			
		}
#endif
		copy_to_user((void*)arg, (void*)(&g_ddr_data), sizeof(T_DDR_DATA));
		break;
		
	case IO_SET_CLR_DDR_TEST:		
		printk("==ioctl: IO_SET_CLR_DDR_TEST(0x%X)== \n", IO_SET_CLR_DDR_TEST);
		ddr_virt = (unsigned char *)normal_virt;	
		for (i=0; i<BUF_SIZE; i+=16)
		{
			ddr_virt[i] = 0;
		}
		break;
		
	case IO_GET_RD_DDR_DATA:		
	case IO_GET_RD_DDR_DATA2:		
		printk("==ioctl: IO_GET_RD_DDR_DATA(0x%X)== \n", IO_GET_RD_DDR_DATA2);
		{
			//unsigned long copy_from_user (void * to, const void __user * from, unsigned long n);
			copy_from_user((void*)(&g_ddr_data), (void*)arg, sizeof(T_DDR_DATA));		
			printk("g_ddr_data.addr:0x%lX \n", g_ddr_data.addr);
			
			if (g_ddr_data.addr > 0)
			{
				//ddr_virt = (unsigned char *)ioremap(g_ddr_data.addr, 4096);		
				ddr_virt = (unsigned char *)phys_to_virt(g_ddr_data.addr);
				for (i=0; i<BUF_SIZE; i++)
				{
					g_ddr_data.buf[i] = ddr_virt[i];  // read data from ddr
				}	
				
				for (i=0; i<BUF_SIZE; i+=16)
				{
					printk("%02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X", \
						   ddr_virt[i],ddr_virt[i+1],ddr_virt[i+2],ddr_virt[i+3],	  \
						   ddr_virt[i+4],ddr_virt[i+5],ddr_virt[i+6],ddr_virt[i+7],   \
						   ddr_virt[i+8],ddr_virt[i+9],ddr_virt[i+10],ddr_virt[i+11],	\
						   ddr_virt[i+12],ddr_virt[i+13],ddr_virt[i+14],ddr_virt[i+15]);			
				}		
				//unsigned long copy_to_user (void __user * to, const void * from, unsigned long n);
				copy_to_user((void*)arg, (void*)(&g_ddr_data), sizeof(T_DDR_DATA));
			}
		}
		break;
		
	case IO_SET_WR_DDR_DATA:
		printk("==ioctl: IO_SET_WR_DDR_DATA(0x%X)== \n", IO_SET_WR_DDR_DATA);
		{
			copy_from_user((void*)(&g_ddr_data), (void*)arg, sizeof(T_DDR_DATA));		
			printk("g_ddr_data.addr:0x%lX \n", g_ddr_data.addr);
			
			if (g_ddr_data.addr > 0)
			{
				ddr_virt = (unsigned char *)phys_to_virt(g_ddr_data.addr);
				for (i=0; i<BUF_SIZE; i++)
				{
					ddr_virt[i] = g_ddr_data.buf[i];  // write data to ddr
				}	
				
				for (i=0; i<BUF_SIZE; i+=16)
				{
					printk("%02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X", \
						   ddr_virt[i],ddr_virt[i+1],ddr_virt[i+2],ddr_virt[i+3],	  \
						   ddr_virt[i+4],ddr_virt[i+5],ddr_virt[i+6],ddr_virt[i+7],   \
						   ddr_virt[i+8],ddr_virt[i+9],ddr_virt[i+10],ddr_virt[i+11],	\
						   ddr_virt[i+12],ddr_virt[i+13],ddr_virt[i+14],ddr_virt[i+15]);			
				}
				
			}
		}
		break;
		
	case IO_GET_DRV_VER:
		printk("==ioctl: IO_GET_DRV_VER(0x%X)== \n", IO_GET_DRV_VER);
		//cur_ver.mainVer = PCI_MAIN_VER;
		//cur_ver.slaveVer = PCI_SLAVE_VER;
		//if(copy_to_user((void *)arg, &cur_ver, sizeof(struct driver_version)))
		{	
		//	ret = -EFAULT;
		}
		printk("DRV_VER: FPGA_PCIE_2022_12_11 \n");
		break;

	//
	// usr get irq_chn-No.
	//
	case IO_GET_IRQ_CHN_NO:
		g_ddr_data.addr = rx_chn;		
		copy_to_user((void*)arg, (void*)(&g_ddr_data), sizeof(T_DDR_DATA));
		break;
		
	//
	// usr get ddr phy_addr
	//
	case IO_GET_DDR_PHY_ADDR:
		copy_from_user((void*)(&g_ddr_data), (void*)arg, sizeof(T_DDR_DATA));
		offset = g_ddr_data.addr;
		g_ddr_data.addr = offset_to_phys_all(offset);
		copy_to_user((void*)arg, (void*)(&g_ddr_data), sizeof(T_DDR_DATA));
		break;

	//
	// tx: ft --> fpga
	//
	case IO_SET_TX_FPGA_CH1:  // for test tx chn1: ft_ddr->fpga_ep
		printk("==ioctl: IO_SET_TX_FPGA_CH1(0x%X)== \n", IO_SET_TX_FPGA_CH1);
		irq_dma_tx_start_chn_1();
		break;
	case IO_SET_TX_FPGA_CH2:  // for test tx chn2: ft_ddr->fpga_ep
		printk("==ioctl: IO_SET_TX_FPGA_CH2(0x%X)== \n", IO_SET_TX_FPGA_CH2);
		irq_dma_tx_start_chn_2();
		break;
	case IO_SET_TX_FPGA_CH3:  // for test tx chn3: ft_ddr->fpga_ep
		printk("==ioctl: IO_SET_TX_FPGA_CH3(0x%X)== \n", IO_SET_TX_FPGA_CH3);
		irq_dma_tx_start_chn_3();
		break;
	case IO_SET_TX_FPGA_CH4:  // for test tx chn4: ft_ddr->fpga_ep
		printk("==ioctl: IO_SET_TX_FPGA_CH4(0x%X)== \n", IO_SET_TX_FPGA_CH4);
		irq_dma_tx_start_chn_4();
		break;
		
	case IO_TEST_TRIG_IRQ:  // for test trig one pcie msi irq
		printk("==ioctl: IO_TEST_TRIG_IRQ(0x%X)== \n", IO_TEST_TRIG_IRQ);
		test_trig_pcie_irq();
		break;		
	case IO_TEST_RD_IRQ_TIME:  // for test trig one pcie msi irq
		printk("==ioctl: IO_TEST_RD_IRQ_TIME(0x%X)== \n", IO_TEST_RD_IRQ_TIME);
		copy_to_user((void*)arg, (void*)(&g_irq_time), sizeof(u32));
		printk("==ioctl: g_irq_time(0x%X)== \n", g_irq_time);
		break;		
	
	default:
		printk(KERN_INFO "unknown ioctl cmd! \n");
		break;
	}
	
	return ret;
	
}



static unsigned int fpga_poll(struct file *file, struct poll_table_struct *wait)
{
    unsigned int mask = 0; 

    poll_wait(file, &irq_msi_waitq, wait);
	
    if (ev_irq_msi == 1)
    {
		ev_irq_msi = 0;
        mask = POLLIN; 
    }
	else
	{	
		mask = 0;
	}

    return mask;
} 

#endif

 
//难点二：启动dma的读写（read和write函数).
static struct file_operations fpga_fops = 
{
	.owner   		=  THIS_MODULE,  
		
	.open   		=  fpga_open,     
	.release 		=  fpga_close,
	
	.read           =  fpga_read,
	.write          =  fpga_write,
	// .llseek         =  fpga_llseek,
	
    // .ioctl		    =  fpga_ioctl,
	.unlocked_ioctl =  fpga_unlocked_ioctl,
	
	.mmap           = fpga_mmap,
	.poll           = fpga_poll,
};
 
static unsigned int fpga_major = 0;
static struct class *fpga_class;

//#define FPGA_DRV_NAME "fpga_drv"

static int fpga_drv_init(void)
{
	int ret;
	
	printk("\n\n---fpga_drv_init---\n");	

	//
	// pci drv register
	//
	ret = pci_register_driver(&fpga_driver);
	if (ret < 0) 
	{
		printk("failed: pci_register_driver\n");
		return ret;
	}
	else
	{
		printk("pci_register_driver ok! \n");
	}
	
	//
	// char_dev drv alloc&init
	//
	ret = alloc_chrdev_region(&fpga_dev.devno, 0, DEVICE_NUMBER, "fpga");
	if (ret < 0)
	{
		printk("failed: register_chrdev_region\n");
		return ret;
	} 
	else
	{
		printk("alloc_chrdev_region ok! \n");
	}
	
	cdev_init(&fpga_dev.cdev, &fpga_fops);
	ret = cdev_add(&fpga_dev.cdev, fpga_dev.devno, DEVICE_NUMBER);
	if (ret < 0) 
	{
		printk("faield: cdev_add\n");
		return ret;
	}	
	else
	{
		printk("cdev_add ok! \n");
	}
	
	fpga_class = class_create(THIS_MODULE, "fpga_pcie");
	fpga_class_dev = device_create(fpga_class, NULL, fpga_dev.devno, NULL, "fpga_pcie"); 

#if 1// for x86 test
	//
	// kmalloc ddr 
	//
	if (fpga_pcie_exist == 0)
	{
		kmalloc_dma_init_all();
	}
#endif
	//
	// init waitqueue
	//
 	init_waitqueue_head(&irq_msi_waitq);
 
	printk("---fpga_drv_init ok!--- \n");
	printk("\n");
	
	return 0;
}
 
static void fpga_drv_exit(void)
{
	printk("\n\n===fpga_drv_exit===\n");
	
	//remove_wait_queue(&irq_msi_waitq, NULL);
	iounmap((void *)bar0_vir);

	//
	// unmap kmalloc ddr
	//
	iounmap_all();
	
	device_destroy(fpga_class, fpga_dev.devno);
	class_destroy(fpga_class);
		
	cdev_del(&(fpga_dev.cdev));
	unregister_chrdev_region(fpga_dev.devno, DEVICE_NUMBER);
	
	pci_unregister_driver(&fpga_driver);
	
	printk("===fpga_drv_exit===\n");
	return;
}
 
module_init(fpga_drv_init);
module_exit(fpga_drv_exit);

//MODULE_LICENSE("Dual BSD/GPL");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("pcie device driver");
MODULE_AUTHOR("James Chen");
MODULE_VERSION("FPGA_PCIE_2022_12_11");


/*
#
# 银河麒麟：Makefile
#
#本机原生编译使用/lib/modules/`uname -r`/build
#

KERNELDIR := /lib/modules/$(shell uname -r)/build

CURRENT_PATH := $(shell pwd)

CPPFLAGS += -include $(KERNELDIR)/include/generated/autoconf.h
EXTRA_CFLAGS +=-Wno-date-time

#要生成的模块名
obj-m := fpga_pcie_drv.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
	
clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
*/

/*
地址空间 长度 描述
0x000_00000000~0x000_1FFFFFFF 512MB QSPI
0x000_20000000~0x000_27FFFFFF 128MB LPC
0x000_28000000~0x000_2FFFFFFF 128MB 设备寄存器地址，除表中单独列出的寄存器以外，其它所有寄存器
0x000_30000000~0x000_39FFFFFF 160MB 调试寄存器空间
0x000_3A000000~0x000_3AFFFFFF 16MB 片内网络内部寄存器空间

0x000_40000000~0x000_7FFFFFFF 1GB PCIe 的配置、 IO 和 MEM32 空间

0x000_80000000~0x000_FFFFFFFF 2GB Memory 空间
0x001_00000000~0x00F_FFFFFFFF 60GB 保留空间
0x010_00000000~0x01F_FFFFFFFF 64GB PCIe 的 MEM64 空间
0x020_00000000~0xFFF_FFFFFFFF 15TB768GB 扩展 Memory 空间


地址空间 长度 描述
0x000_28000000~0x000_28000FFF 4KB UART0
0x000_28001000~0x000_28001FFF 4KB UART1
0x000_28002000~0x000_28002FFF 4KB UART2
0x000_28003000~0x000_28003FFF 4KB UART3
0x000_28004000~0x000_28004FFF 4KB GPIO0
0x000_28005000~0x000_28005FFF 4KB GPIO1
0x000_28006000~0x000_28006FFF 4KB I2C0
0x000_28007000~0x000_28007FFF 4KB I2C1
0x000_28008000~0x000_28008FFF 4KB I2C2
0x000_28009000~0x000_28009FFF 4KB I2C3
0x000_2800A000~0x000_2800BFFF 8KB WDT0
0x000_2800C000~0x000_2800CFFF 4KB SPIM0
0x000_28012000~0x000_28012FFF 4KB RAS
0x000_28013000~0x000_28013FFF 4KB SPIM1
0x000_28014000~0x000_28014FFF 4KB QSPI 寄存器
0x000_28016000~0x000_28017FFF 8KB WDT1


腾锐 D2000 是一款面向桌面应用的高性能通用 8 核处理器。每 2 个核构成 1
个处理器核簇（Cluster），并共享 L2 Cache。
存储系统包含 Cache 子系统和 DDR， I/O 系统包含 PCIe、高速 IO 子系统、
千兆位以太网 GMAC 和低速 IO 子系统。

腾锐 D2000 处理器的主要技术特征如下：
	⚫ 兼容 ARM v8 64 位指令系统，兼容 32 位指令
	⚫ 支持单精度、双精度浮点运算指令
	⚫ 支持 ASIMD 处理指令
	⚫ 集成 2 个 DDR 通道，支持 DDR4 和 LPDDR4，可对 DDR 存储数据进行
	实时加密
	⚫ 集成 34 Lane PCIE3.0 接口： 2 个 X16（每个可拆分成 2 个 X8）， 2 个 X1
	⚫ 集成 2 个 GMAC， RGMII 接口，支持 10/100/1000 自适应
	⚫ 集成 1 个 SD 卡控制器，兼容 SD 2.0 规范
	⚫ 集成 1 个 HDAudio，支持音频输出，可同时支持最多 4 个 Codec
	⚫ 集成 SM2、 SM3、 SM4、 SM9 模块
	⚫ 集成 4 个 UART， 1 个 LPC， 32 个 GPIO， 4 个 I2C， 1 个 QSPI， 2 个通用
SPI， 2 个 WDT， 16 个外部中断（和 GPIO 共用 IO）
⚫ 集成 2 个温度传感器

功能特征总结如表 1-1 所示。

表 3-1 功能描述
硬件特性 说明
Core 四个 cluster，八个核 FTC663 核
L2Cache 8MB 每两个 core 共享 2M L2 Cache
L3Cache 4MB 分为 8 个 Bank

存储控制器 2 个 72 位 DDR4（其中 8 位 ECC）
支持 X4、 X8、 X16、 X32 等类型 DDR4 颗粒；支持
UDIMM、 SODIMM、 RDIMM 等类型 DDR4 模组；支
持 X16 单通道和双通道 LPDDR4 颗粒；

外设
PCIe3.0 2 个 X16（每个可拆分成 2 个 X8）， 2 个 X1
千兆以太网控制器 2 个， RGMII 接口， 10/100/1000 自适应
SD 卡控制器 1 个，兼容 SD 2.0
HDAudio 1 个，支持 44.1/48/96/192KHz 的音频数据采样率
UART 4 个串口
LPC 1 个 Low Pin Count 接口
GPIO 32 个 GPIO 接口，分为 A、 B、 C、 D 四组，每组 8 位
I2C 4 个 I2C 接口，支持 Master 或 Slave 模式
QSPI 1 个 QSPI Flash 接口
WDT 2 个，一个 Secure，一个 NonSecure
CAN 3 个 CAN 控制器，兼容 CAN2.0 标准协议
SPI 2 个通用 SPI master 接口
*/

/*
fpga_pcie_endpiont cfg_space_addr:
----------------------------------
root@zkjr-D2000:/home/zkjr/test2# ./devmem4 0x41000000 256 
/dev/mem opened.
Memory mapped at address 0x7f9deb1000.
0x41000000: 702410EE  00100406  05800000  00000000 
0x41000010: 58000000  00000000  00000000  00000000 
0x41000020: 00000000  00000000  00000000  000710EE 
0x41000030: 00000000  00000040  00000000  000001FF 
0x41000040: 78034801  00000008  00816005  29A30040 
0x41000050: 00000000  00000000  00000000  00000000 
0x41000060: 00020010  00008E02  00012810  0003F442 
0x41000070: 10220000  00000000  00000000  00000000 
0x41000080: 00000000  00000002  00000000  00000000 
0x41000090: 00010002  00000000  00000000  00000000 
0x410000A0: 00000000  00000000  00000000  00000000 
0x410000B0: 00000000  00000000  00000000  00000000 
0x410000C0: 00000000  00000000  00000000  00000000 
0x410000D0: 00000000  00000000  00000000  00000000 
0x410000E0: 00000000  00000000  00000000  00000000 
0x410000F0: 00000000  00000000  00000000  00000000
*/

